package com.example.controllers;

import com.example.auxiliar.SortCreator;
import com.example.dtos.ItemToPurchaseDTO;
import com.example.dtos.ProductDTO;
import com.example.exceptions.MissingAttributesException;
import com.example.exceptions.WrongDateFormatException;
import com.example.models.ItemToPurchase;
import com.example.models.Product;
import com.example.services.ProductService;
import com.example.validators.HibernateValidator;
import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.hateoas.Resources;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;

@RestController
@RequestMapping("/products")
public class ProductController {
	
		@Autowired
		ProductService productService;
		
		@Autowired
		private Mapper mapper;
		
		HibernateValidator<Product, ProductDTO> validator = new HibernateValidator<Product,ProductDTO>();
		SortCreator sortCreator = new SortCreator();
		
		/*****************************GET*******************************************/
		//READ
		@RequestMapping(value="/{productId}", method = RequestMethod.GET)
		@ResponseStatus(value= HttpStatus.OK)
		public ProductDTO productWithId(@PathVariable String productId) { //mongo
//				public ProductDTO productWithId(@PathVariable Long productId) {
			return mapper.map(productService.find(productId),ProductDTO.class);
		}
				 
		 @RequestMapping(method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
		 @ResponseStatus(value= HttpStatus.OK)
		  public Resources<ProductDTO> findAll(
				  @RequestParam(value = "page", required = false) Integer page,
				  @RequestParam(value = "size", required = false) Integer size,
				  @RequestParam(value = "sort", required = false) String sort){
			 Sort sortObject = sortCreator.getSortObject(sort);
			 Collection<ProductDTO> result= new ArrayList<ProductDTO>();
			 productService.findProducts(page,size,sortObject).stream().forEach((p)->{
				 ProductDTO pdto = mapper.map(p,ProductDTO.class);
				 pdto.add(linkTo(methodOn(ProductController.class).productWithId(p.getId())).withSelfRel());
				 result.add(pdto);
				 });
			 Resources<ProductDTO> resources = new Resources<ProductDTO>(result);
			 return addPaginationLinks(resources, page,  size, sort);
		 }
		 
		 private Resources<ProductDTO> addPaginationLinks(Resources<ProductDTO> resources, Integer page, Integer size, String sort){
			 resources.add(linkTo(methodOn(ProductController.class).findAll(page,size,sort)).withSelfRel());
			 if(page!=null && size!=null){
				 if(page>0) {
					 resources.add(linkTo(methodOn(ProductController.class).findAll(1,size,sort)).withRel("first"));
					 resources.add(linkTo(methodOn(ProductController.class).findAll(page-1,size,sort)).withRel("previous"));
				 }
				 resources.add(linkTo(methodOn(ProductController.class).findAll(page+1,size,sort)).withRel("next"));
//				 resources.add(linkTo(methodOn(ProductController.class).findAll(1,pageSize,condition,attribute)).withRel("last")); //NEED A COUNT ON THE DB!!
			 }
			 return resources;
		 }

		@RequestMapping(value="/category/{category}",method = RequestMethod.GET)
		 @ResponseStatus(value= HttpStatus.OK)
		 public Collection<ProductDTO> productsWithCategory(@PathVariable String category) {
			 Collection<ProductDTO> result= new ArrayList<ProductDTO>();
			 productService.productsWithCategory(category).stream().forEach((p)->{result.add(mapper.map(p,ProductDTO.class));});
		     return result;
		 }
		 
		 @RequestMapping(value="/name/{name}",method = RequestMethod.GET)
		 @ResponseStatus(value= HttpStatus.OK)
		 public Collection<ProductDTO> productsWithName(@PathVariable String name) {
			 Collection<ProductDTO> result= new ArrayList<ProductDTO>();
			 productService.productsWithName(name).stream().forEach((p)->{result.add(mapper.map(p,ProductDTO.class));});
		     return result;
		 }
		 
		 @RequestMapping(value="/stockGreatherThan/{quantity}",method = RequestMethod.GET)
		 @ResponseStatus(value= HttpStatus.OK)
		 public Collection<ProductDTO> productsWithStockGreaterThan(@PathVariable int quantity) {
		    	Collection<ProductDTO> result= new ArrayList<ProductDTO>();
		    	productService.productsWithStockGreaterThan(quantity).stream().forEach((p)->{result.add(mapper.map(p,ProductDTO.class));});
			    return result;
		 }
		 
		 @RequestMapping(value="/stockLessThan/{quantity}",method = RequestMethod.GET)
		 @ResponseStatus(value= HttpStatus.OK)
		 public Collection<ProductDTO> productsWithStockLessThan(@PathVariable int quantity) {
		    	Collection<ProductDTO> result= new ArrayList<ProductDTO>();
		    	productService.productsWithStockLessThan(quantity).stream().forEach((p)->{result.add(mapper.map(p,ProductDTO.class));});
			    return result;
		 }
		 
		 @RequestMapping(value="/stockLessThanAndGreaterThan/{inf}/{sup}",method = RequestMethod.GET)
		 @ResponseStatus(value= HttpStatus.OK)
		 public Collection<ProductDTO> productsWithStockLessThanAndStockGreaterThan(@PathVariable int inf,@PathVariable int sup) {
		    	Collection<ProductDTO> result= new ArrayList<ProductDTO>();
		    	productService.findByStockBetween(inf,sup).stream().forEach((p)->{result.add(mapper.map(p,ProductDTO.class));});
			    return result;
		 }
		 
		 @RequestMapping(value="/dateBetween/{date1String}/{date2String}",method = RequestMethod.GET)
		 @ResponseStatus(value= HttpStatus.OK)
		 public Collection<ProductDTO> productsWithExpirationDateBetween(
				 @PathVariable String date1String, @PathVariable String date2String) {
			 Date date1,date2;
			 SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
			 try{
				 date1 = formatter.parse(date1String);
				 date2 = formatter.parse(date2String);
			 }catch(Exception e){
				 throw new WrongDateFormatException();
			 }
			 Collection<ProductDTO> result= new ArrayList<ProductDTO>();
			 productService.productsWithExpirationDateBetween(date1,date2).stream().forEach((p)->{result.add(mapper.map(p,ProductDTO.class));});
		     return result;
		 }
		
		
		/*****************************POST*******************************************/
		//CREATE
		@RequestMapping(method = RequestMethod.POST)
		@ResponseStatus(value= HttpStatus.CREATED)
		public ProductDTO createProduct(@RequestBody ProductDTO productDTO){
			//security is granted by springsec 
			Product newProduct = productService.createProduct(checkAndCreateProduct(productDTO)); 
			ProductDTO savedEntityDTO = mapper.map(newProduct, ProductDTO.class);
			savedEntityDTO.add(linkTo(methodOn(ProductController.class).productWithId(newProduct.getId())).withSelfRel());
			return savedEntityDTO;
		}
		
		
		/*****************************PUT*******************************************/
		//UPDATE
		@RequestMapping(method = RequestMethod.PUT, value="/{productId}")
		@ResponseStatus(value= HttpStatus.OK)
//		public void editProduct(@PathVariable Long productId, @RequestBody ProductDTO productDTO){
		public void editProduct(@PathVariable String productId, @RequestBody ProductDTO productDTO){ //mongo
			Product newProduct = checkAndCreateProduct(productDTO);
			productService.editProduct(productId,newProduct);
		  }
		 
		/*****************************DELETE*******************************************/
		 //DELETE
		 @RequestMapping(method = RequestMethod.DELETE, value="/{productId}")
		 @ResponseStatus(value= HttpStatus.NO_CONTENT)
		 public void deleteProduct(@PathVariable("productId") String productId){ //mongo
//		 public void deleteProduct(@PathVariable("productId") Long productId){
			productService.deleteProduct(productId);
		 } 
		 		 

		 
		 //AUXILIAR METHODS
		 private Product checkAndCreateProduct(ProductDTO productDTO) throws MissingAttributesException{
			validator.validateObject(productDTO);
			return mapper.map(ProductDTO.checkData(productDTO),Product.class);
			/** checkData(productDTO) checks that the json has all the fields needed, not checked in the hibernate validator **/
		 }
		 
		 private ItemToPurchase checkAndCreateItem(ItemToPurchaseDTO itemDTO) throws MissingAttributesException{
			 ItemToPurchaseDTO.checkFields(itemDTO);
			 return mapper.map(itemDTO,ItemToPurchase.class);
		 }

	}